Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix perturbNormal2Arb() bug #13716

Merged
merged 1 commit into from
Mar 28, 2018
Merged

Conversation

WestLangley
Copy link
Collaborator

As discussed in #7094

@donmccurdy
Copy link
Collaborator

🎉

before after
screen shot 2018-03-28 at 11 04 12 am screen shot 2018-03-28 at 11 03 37 am

@WestLangley
Copy link
Collaborator Author

@donmccurdy Unrelated: Any idea why the model has .normalScale.x = - 1? It is more common to have to flip .normalScale.y, but flipping y does not appear to be required here.

@donmccurdy
Copy link
Collaborator

donmccurdy commented Mar 28, 2018

From GLTFLoader.js:1992 —

// Normal map textures use OpenGL conventions:
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#materialnormaltexture
if ( material.normalScale ) {

	material.normalScale.x = - material.normalScale.x;

}

EDIT: Related to #11315.

@bhouston
Copy link
Contributor

Wohoo! Thanks for making the PR @WestLangley and improving the solution to be more precise.

@WestLangley
Copy link
Collaborator Author

WestLangley commented Mar 28, 2018

@donmccurdy I do not think GLTFLoader should be doing this.

// Normal map textures use OpenGL conventions:
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#materialnormaltexture
if ( material.normalScale ) {

	material.normalScale.x = - material.normalScale.x;

}

A reasonable guess is the model was created for BabylonJS. If a user wants to use the model in three.js, the user should set normalScale.x = - 1.

@mrdoob mrdoob added this to the r92 milestone Mar 28, 2018
@mrdoob mrdoob merged commit 7f0bcc4 into mrdoob:dev Mar 28, 2018
@mrdoob
Copy link
Owner

mrdoob commented Mar 28, 2018

Thanks!

@mrdoob
Copy link
Owner

mrdoob commented Mar 28, 2018

A reasonable guess is the model was created for BabylonJS. If a user wants to use the model in three.js, the user should set normalScale.x = - 1.

Uh oh...

@donmccurdy
Copy link
Collaborator

donmccurdy commented Mar 28, 2018

A reasonable guess is the model was created for BabylonJS.

I don't think that's the case, but would be more confident if I knew the answer to #11315. 😅

If the model uses BabylonJS's normal handedness, it is an invalid glTF file. I'll look into this in a bit.

@donmccurdy
Copy link
Collaborator

On closer look, BabylonJS inverts normal map X when using their default left-handed system.

@WestLangley WestLangley deleted the dev-perturb_normal branch March 28, 2018 21:28
@mrdoob
Copy link
Owner

mrdoob commented Mar 28, 2018

@donmccurdy After this PR the boombox normalmap seems to have been inverted. That material.normalScale.x = - material.normalScale.x; line may have been a hack to work around the problem this PR solved.

@mrdoob
Copy link
Owner

mrdoob commented Mar 28, 2018

I've tested with NormalTangentTest and NormalTangentMirrorTest and, after this PR and with this code they look correct:

if ( material.normalScale ) {

	material.normalScale.y = - material.normalScale.y;

}

screen shot 2018-03-28 at 3 19 44 pm

screen shot 2018-03-28 at 3 20 21 pm

🤔

@mrdoob
Copy link
Owner

mrdoob commented Mar 28, 2018

This PR is great. The gun in webgl_materials_standard was also wrong.

Before:

screen shot 2018-03-28 at 3 33 54 pm

After:

screen shot 2018-03-28 at 3 34 19 pm

@WestLangley
Copy link
Collaborator Author

This is all good news, but the loader should not change the model parameters. The correct fix is to properly set normalScale in the model -- or alternatively, reset it in the loader callback.

@mrdoob
Copy link
Owner

mrdoob commented Mar 28, 2018

Agreed. webgl_materials_standard does not use GLTFLoader by the way, it's a obj model.

@donmccurdy
Copy link
Collaborator

donmccurdy commented Mar 28, 2018

This is all good news, but the loader should not change the model parameters. The correct fix is to properly set normalScale in the model ...

The handedness of the three.js normal map system remains unanswered in #11315. If it is not the same as the OpenGL convention (which all glTF assets must use) then inverting an axis of normalScale in GLTFLoader to convert seems reasonable, yes? BabylonJS does precisely that, because they're aware their system does not match OpenGL or glTF convention.

@mrdoob
Copy link
Owner

mrdoob commented Mar 29, 2018

A reasonable guess is the model was created for BabylonJS. If a user wants to use the model in three.js, the user should set normalScale.x = - 1.

Actually, this shouldn't be the problem. Otherwise this would be a bug in the GLTF spec. The model should look identical in any engine.

@bhouston
Copy link
Contributor

I would suggest standardizing on Unity and UE4 style normal map interpretation. I believe they are the same thing.

@WestLangley
Copy link
Collaborator Author

WestLangley commented Mar 29, 2018

What is three.js's normal map handedness convention?

I would say the three.js tangent space coordinate convention is positive-x is to the right, and positive-y is up.

@bhouston
Copy link
Contributor

Hilarious, UE4 and Unity have different conventions - in fact the supporters of each of the two formats have equal supporters:

http://www.werwackfx.com/index.php/graphictools/graphicsarticles/graphics-articles/45-normalmapsart

I think you should then have a parameter to support both, call one OpenGL and the other DirectX, and have documentation that lists the various programs that support these two styles. OpenGL might as well be the default.

@WestLangley
Copy link
Collaborator Author

I think you should then have a parameter to support both

We have a parameter that supports both: .normalScale. It's purpose is to support normal maps authored for other engines. Of course, it also serves to reduce, or increase, the normal map's effect.

@bhouston
Copy link
Contributor

My recommendation is a public parameter called normalMapStyle. It would be separate from normalMapScale, which technically should be a scalar anyhow.

Thus you would have one style parameter and one scale parameter. It can still be converted into a Vec3 when passed to the shader as it is right now, but my suggested parameterization is a more usable API because it matches how developers think about the problem, rather than expecting developers to jump through non-obvious mental hoops in order to figure out ThreeJS - see @donmccurdy comment. Don is smart, but ThreeJS is confusing him here.

@mrdoob
Copy link
Owner

mrdoob commented Mar 29, 2018

Another option would be to document the issue whenever we figure out exactly what is the right normalScale configuration to use on each case.

@bhouston
Copy link
Contributor

bhouston commented Mar 29, 2018

Well, I just try to follow other engines, like Unity and UE4, which expose a single scalar.

The behavior when normalScale = Vec2(1,1), ThreeJS operates with brighter green being up, which is OpenGL style.

I can understand this:

material.normalStyle = THREE.DirectXNormalStyle;
material.normalScale = 0.8;

Much easier than this:

// material.normalScale = new THREE.Vec2( -0.8, 0.8 ); -- oops, got it wrong, should have flipped G, but R.
material.normalScale = new THREE.Vec2( 0.8, -0.8 );

And it also prevents people from doing just incorrect things like this:

material.normalScale = new THREE.Vec2( -1.0, 0.5 );

@WestLangley
Copy link
Collaborator Author

normalMapScale, which technically should be a scalar anyhow.

Agreed, normalScale really should be a scalar.

I think the only reason it is not a scalar is to accommodate normal maps authored assuming a different tangent-space coordinate convention.

material.normalScale.set( 0.5, - 0.5 );

@donmccurdy
Copy link
Collaborator

Quick patch in #13784 for r92.

Do we want to try the normalScale / normalStyle change for r93, or just document this?

@WestLangley
Copy link
Collaborator Author

I suggest retaining the current pattern, as it is flexible.

material.normalScale.set( 0.5, - 0.5 );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants